/**
  ******************************************************************************
  * File Name          : storage.c
  * Description        : 存储相关,为参数系统提供底层存储架构和方法
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "bsp.h"
#include "storage.h"
#include <string.h>
#include <stdio.h>

const static Sto_CmdStru init_allitem = 
{
  .cmd = STO_CMD_INIT_ALLITEMS,
  .idx = 0,
  .dat = {0},
};

uint8_t sto_ckcb_general_bool(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if((dat.ut16 != 0) && (dat.ut16 != 1))
  {
    return 2;
  }
  
  return 0;
}

uint8_t sto_ckcb_datatime(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  return sys_check_datatime(&dat.dt);
}

uint8_t sto_ckcb_ziptime32(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.ut32 > 235959u)
  {
    return 1;
  }

  if(((dat.ut32%10000)/100) > 59)
  {
    return 2;
  }
  
  if((dat.ut32%100) > 59)
  {
    return 3;
  }

  return 0;
}

uint8_t sto_ckcb_general_ut8(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.ut8 < min.ut8)
  {
    SysDbgLog("data:%d, min:%d\r\n", dat.ut8, min.ut8);
    return 2;
  }
  
  if(dat.ut8 > max.ut8)
  {
    SysDbgLog("data:%d, max:%d\r\n", dat.ut8, max.ut8);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_ut16(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.ut16 < min.ut16)
  {
    SysDbgLog("data:%d, min:%d\r\n", dat.ut16, min.ut16);
    return 2;
  }

  if(dat.ut16 > max.ut16)
  {
    SysDbgLog("data:%d, max:%d\r\n", dat.ut16, max.ut16);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_ut16_with0(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.ut16 == 0)
  {
    return 0;
  }

  if(dat.ut16 < min.ut16)
  {
    SysDbgLog("data:%d, min:%d\r\n", dat.ut16, min.ut16);
    return 2;
  }
  
  if(dat.ut16 > max.ut16)
  {
    SysDbgLog("data:%d, max:%d\r\n", dat.ut16, max.ut16);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_st16(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.st16 < min.st16)
  {
    SysDbgLog("data:%d, min:%d\r\n", dat.st16, min.st16);
    return 2;
  }
  
  if(dat.st16 > max.st16)
  {
    SysDbgLog("data:%d, max:%d\r\n", dat.st16, max.st16);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_ut32(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.ut32 < min.ut32)
  {
    SysDbgLog("data:%d, min:%d\r\n", dat.ut32, min.st32);
    return 2;
  }
  
  if(dat.ut32 > max.ut32)
  {
    SysDbgLog("data:%d, max:%d\r\n", dat.ut32, max.ut32);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_st32(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.st32 < min.st32)
  {
    SysDbgLog("data:%d, min:%d\r\n", (int32_t )dat.st32, (int32_t )min.st32);
    return 2;
  }
  
  if(dat.st32 > max.st32)
  {
    SysDbgLog("data:%d, max:%d\r\n", (int32_t )dat.st32, (int32_t )max.st32);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_ut64(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  if(dat.ut64 < min.ut64)
  {
    SysDbgLog("data:%d, min:%d\r\n", (int32_t )dat.ut64, (int32_t )min.ut64);
    return 2;
  }
  
  if(dat.ut64 > max.ut64)
  {
    SysDbgLog("data:%d, max:%d\r\n", (int32_t )dat.ut64, (int32_t )max.ut64);
    return 3;
  }
  
  return 0;
}

uint8_t sto_ckcb_general_ip(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  /* ip暂不做检查 因为每个字节的范围就是0~255 若想检查 可排除广播地址等 */
  return 0;
}

uint8_t sto_ckcb_general_mac(const Sto_CellUnion dat, const Sto_CellUnion max, const Sto_CellUnion min)
{
  /* mac暂不做检查 因为每个字节的范围就是0~255 若想检查 可排除广播 组播等mac 等 */
  return 0;
}

/**
  * @brief  根据页面号和焦点号从"维护表里"遍历并找到其"维护结构体"
  * @param  page ui页面号
  * @param  focus ui页面内的焦点号
  * @param  mttb 维护表
  * @retval "维护结构体"地址或NULL
  */
const Sto_MaintainStru *sto_find_mt_with_uipos(uint8_t page, uint8_t focus, const Sto_MaintainTableStru *mttb)
{
  uint8_t i;

  for(i=0; i<mttb->tablesize; i++)
  {
    if((mttb->maint[i].page == page) && (mttb->maint[i].focus == focus))
    {
      return &(mttb->maint[i]);
    }
  }

  return NULL;
}

/**
  * @brief  根据索引号从"维护表里"遍历并找到其"维护结构体"
  * @param  idx 项目索引号
  * @param  mttb 维护表
  * @retval "维护结构体"地址或NULL
  */
static inline const Sto_MaintainStru *sto_find_mt_with_idx(uint8_t idx, const Sto_MaintainTableStru *mttb)
{
  uint8_t i;
  
  /* 从维护表内找 */
  for(i=0; i<mttb->tablesize; i++)
  {
    if(mttb->maint[i].idx == idx)
    {
      return &(mttb->maint[i]);
    }
  }

  return NULL;
}

/**
  * @brief  初始化单个项目并存储和更新目标
  * @param  idx 项目索引
  * @param  mttb
  * @retval None
  */
uint8_t sto_init_item(uint8_t idx, const Sto_MaintainTableStru *mttb, void *dest)
{
  Sto_DataStru temp;
  const Sto_MaintainStru *pmt = NULL;

  SysDbgLog("sto_init_item idx:%d\r\n", idx);
  
  if(mttb == NULL)
  {
    SysDbgLog("sto_init_item err 1 idx:%d\r\n", idx);
    
    return 1;
  }

  pmt = sto_find_mt_with_idx(idx, mttb);
  if(pmt == NULL)
  {
    SysDbgLog("sto_init_item cannot find item idx:%d\r\n", idx);
    return 2;
  }

  temp.para = pmt->init;

  temp.crc16 = sys_crc16(temp.para.ua8, sizeof(temp.para));

  if(mb85rs64_write_bytes(pmt->addr, (const uint8_t *)(&temp), sizeof(temp)) != 0)
  {
    SysDbgLog("sto_init_item WRITE err, idx:%d\r\n", idx);
    return 3;
  }

  if(pmt->upcb != NULL)
  {
    pmt->upcb(idx, temp.para, dest);
  }
  
  return 0;
}

/**
  * @brief  从存储中读取一个项目(并校验)
  * @param  None
  * @retval None
  */
uint8_t sto_get_item(uint8_t idx, const Sto_MaintainTableStru *mttb, void *dest)
{
  uint8_t err = 0xff;
  Sto_DataStru temp;
  const Sto_MaintainStru *pmt = NULL;

  SysDbgLog("sto_get_item idx:%d\r\n", idx);
  
  if(mttb == NULL)
  {
    SysDbgLog("sto_get_item err 1 idx:%d\r\n", idx);
    
    return 1;
  }
  
  pmt = sto_find_mt_with_idx(idx, mttb);
  if(pmt == NULL)
  {
    SysDbgLog("sto_get_item cannot find item idx:%d\r\n", idx);
    return 2;
  }

  /* 从fram中读取至临时缓存 */
  if(mb85rs64_read_bytes(pmt->addr, (uint8_t *)(&temp), sizeof(temp)) != 0)
  {
    SysDbgLog("sto_get_item READ err, idx:%d\r\n", idx);
    return 3;
  }
  
  if(temp.crc16 != sys_crc16(temp.para.ua8, sizeof(temp.para)))
  {
    SysDbgLog("sto_get_item CRC err, idx:%d\r\n", idx);
    return 4;
  }
  
  if(pmt->ckcb != NULL)
  {
    err = pmt->ckcb(temp.para, pmt->max, pmt->min);
    if(err != 0)
    {
      SysDbgLog("sto_get_item CHECK err:%d, idx:%d\r\n", err+3, idx);
      return (err+4);
    }
  }
  
  if(pmt->upcb != NULL)
  {
    pmt->upcb(idx, temp.para, dest);
  }
  
  return 0;
}

/**
  * @brief  向存储中写入一个项目(并校验)
  * @param  None
  * @retval None
  */
uint8_t sto_set_item(uint8_t idx, const Sto_CellUnion *dat, const Sto_MaintainTableStru *mttb, void *dest)
{
  uint8_t err = 0xff;
  Sto_DataStru temp;
  const Sto_MaintainStru *pmt = NULL;

  SysDbgLog("sto_set_item idx:%d\r\n", idx);
  
  if(mttb == NULL)
  {
    SysDbgLog("sto_set_item err 1 idx:%d\r\n", idx);
    return 1;
  }
  
  pmt = sto_find_mt_with_idx(idx, mttb);
  if(pmt == NULL)
  {
    SysDbgLog("sto_set_item cannot find item idx:%d\r\n", idx);
    return 2;
  }

  temp.para = *dat;
  
  if(pmt->ckcb != NULL)
  {
    err = pmt->ckcb(temp.para, pmt->max, pmt->min);
    if(err != 0)
    {
      SysDbgLog("sto_set_item err:%d, idx:%d\r\n", err+3, idx);
      return (err+3);
    }
  }
  
  temp.crc16 = sys_crc16(temp.para.ua8, sizeof(temp.para));
  
  if(mb85rs64_write_bytes(pmt->addr, (const uint8_t *)&temp, sizeof(temp)) != 0)
  {
    SysDbgLog("sto_set_item err 2 idx:%d\r\n", idx);
    return 3;
  }
  
  if(pmt->upcb != NULL)
  {
    pmt->upcb(idx, temp.para, dest);
  }
  
  return 0;
}

/**
  * @brief  按"索引表"内所有项目进行初始化
  * @param  mttb "维护表"地址
  * @retval None
  */
uint8_t sto_init_allitems(const Sto_MaintainTableStru *mttb)
{
  uint8_t idx;
  
  if((mttb == NULL) || (mttb->maint == NULL))
  {
    return 1;
  }
  
  /* 根据存储索引将参数取出, 存储索引内没有的不会被取出 */
  for(idx=0; idx<mttb->saveidxend; idx++)
  {
    if(sto_init_item(idx, mttb, NULL) != 0)
    {
      /* 此处不退出, 出错了接着下一个 */
    }
  }

  return 0;
}

/**
  * @brief  从"维护表"中,取出"索引表"内所有项目
  * @param  mttb "维护表"地址
  * @retval None
  */
uint8_t sto_get_allitems(const Sto_MaintainTableStru *mttb)
{
  uint8_t idx;
  
  if((mttb == NULL) || (mttb->maint == NULL))
  {
    return 1;
  }
  
  /* 根据存储索引将参数取出, 存储索引内没有的不会被取出 */
  for(idx=0; idx<mttb->saveidxend; idx++)
  {
    if(sto_get_item(idx, mttb, NULL) != 0)
    {
      sto_init_item(idx, mttb, NULL);
      /* 此处不退出, 出错了接着下一个 */
    }
  }
  
  return 0;
}

void sto_update_item(uint8_t idx, const Sto_CellUnion *dat, TickType_t mxdelay)
{
  Sto_CmdStru stg_scmd;
  
  stg_scmd.cmd = STO_CMD_SET_ITEM;
  stg_scmd.idx = idx;
  stg_scmd.dat = *dat;
  xQueueSend(storage_qhd, &stg_scmd, mxdelay);
}

void sto_reset_allitems(TickType_t mxdelay)
{
  xQueueSend(storage_qhd, &init_allitem, mxdelay);
}
